<template>
    <div class="file-upload">
        <div class="file-upload-container" @click.prevent="triggerUpload" v-bind="$attrs">
            <slot v-if="fileStatus === 'loading'" name="loading">
                <!-- <button class="btn btn-primary" disabled>正在上传...</button> -->
            </slot>
            <!-- 这里用到了作用域插槽 -->
            <slot v-else-if="fileStatus === 'success'" name="uploaded" :uploadedData="uploadedData">
                <!-- <button class="btn btn-primary">上传成功</button> -->
            </slot>
            <slot v-else name="default">
                <!-- <button class="btn btn-primary">点击上传</button> -->
            </slot>
        </div>
        <input 
            type="file"
            class="file-input d-none"
            ref="fileInput"
            @change="handleFileChange"
        >
    </div>
</template>
<script lang="ts">
import {defineComponent,PropType,ref,watch} from 'vue'
import axios from 'axios'
type UploadStatus = 'ready' | 'loading' | 'success' | 'error'   //定义上传的四个状态
type CheckFunction = (file:File)=>boolean
export default defineComponent({
    props:{
        action:{    //发送请求的路由名
            type:String,
            reqired:true
        },
        beforeUpload:{  //检查上传的图片是否符合要求，CreatePost.vue会传过来一个验证函数
            type:Function as PropType<CheckFunction>
        },
        uploaded:{  //接收图片信息，是一个对象
            type:Object
        }
    },
    inheritAttrs:false, //不要继承默认的属性
    emit:['file-uploaded','file-uploaded-error'],
    setup(props,context){
        const fileInput = ref<null | HTMLInputElement>(null)    //这里的fileInput拿到的是input组件
        //如果传过来的有图片说明已经上传过，上传状态为success
        const fileStatus=ref<UploadStatus>(props.uploaded ? 'success': 'ready') 
        const uploadedData = ref(props.uploaded)    //获取到传过来的图片对象
        watch(()=>props.uploaded,(newValue)=>{  //由于setup只执行一次，所以用watch监视，一上传就执行
            fileStatus.value = 'success'    //代表已上传成功
            uploadedData.value = newValue   //获取到上传的图片信息
        })
        const triggerUpload = ()=>{ //点击按钮弹出选择图片框
            if(fileInput.value){    //如果拿到了input的HTML元素的值
                fileInput.value.click()
            }
        }
        const handleFileChange = (e:Event)=>{   //上传图片时执行的函数
            const currentTarget = e.target as HTMLInputElement
            if(currentTarget.files){    //如果选择了文件
                const files = Array.from(currentTarget.files)
                if(props.beforeUpload){ //有传递过来的验证图片是否符合要求的方法
                    const result = props.beforeUpload(files[0]) //result为函数返回值，boolean类型
                    if(!result){    //不满足上传的条件，直接返回结束
                        return
                    }
                }
                fileStatus.value='loading'
                const formData = new FormData()
                formData.append('file',files[0])    //将文件加入formData中
                axios.post(props.action as string,formData,{    //向后端传送图片
                    headers:{
                        'Content-Type':'multipart/form-data'    //上传二进制文件需要将头部的Content-Type设置一下
                    }
                }).then(resp=>{
                    fileStatus.value='success'
                    uploadedData.value=resp.data
                    context.emit('file-uploaded',resp.data)//传给父组件，显示用户上传的文件
                }).catch((error)=>{
                    fileStatus.value='error'
                    context.emit('file-uploaded-error',{error})
                }).finally(()=>{
                    if(fileInput.value){    //上传失败了，但是fileInput代表的input元素中有值，需要将里面的值清空
                        fileInput.value.value=''
                    }
                })
            }       
        } 
        return {
            fileInput,
            triggerUpload,
            fileStatus,
            handleFileChange,
            uploadedData
        }
    }
})
</script>